<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多爬虫管理系统</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        :root {
            --primary: #4361ee;
            --secondary: #3f37c9;
            --success: #4cc9f0;
            --warning: #f72585;
            --dark: #212529;
            --light: #f8f9fa;
            --gray: #6c757d;
            --light-gray: #e9ecef;
            --border-radius: 8px;
            --shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            --transition: all 0.3s ease;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background-color: #f5f7fb;
            color: var(--dark);
            line-height: 1.6;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            padding: 20px;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
            padding: 20px;
            background: linear-gradient(135deg, var(--primary), var(--secondary));
            color: white;
            border-radius: var(--border-radius);
            box-shadow: var(--shadow);
        }

        header h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
        }

        header p {
            font-size: 1.1rem;
            opacity: 0.9;
        }

        section {
            background: white;
            border-radius: var(--border-radius);
            box-shadow: var(--shadow);
            margin-bottom: 30px;
            overflow: hidden;
        }

        h2 {
            padding: 15px 20px;
            background-color: var(--light-gray);
            border-bottom: 1px solid #dee2e6;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        h2 i {
            font-size: 1.5rem;
        }

        /* 爬虫控制面板 */
        .crawler-controls {
            padding: 20px;
        }

        .crawler-buttons {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 15px;
            margin-bottom: 20px;
        }

        .crawler-btn {
            background: white;
            border: 2px solid var(--light-gray);
            border-radius: var(--border-radius);
            padding: 15px;
            text-align: center;
            cursor: pointer;
            transition: var(--transition);
            display: flex;
            flex-direction: column;
            align-items: center;
            position: relative;
        }

        .crawler-btn:hover {
            transform: translateY(-3px);
            box-shadow: var(--shadow);
            border-color: var(--primary);
        }

        .crawler-btn.test {
            border: 2px dashed var(--gray);
        }

        .btn-icon {
            font-size: 2.5rem;
            margin-bottom: 10px;
        }

        .btn-text {
            font-size: 1.2rem;
            font-weight: 600;
            margin-bottom: 5px;
        }

        .btn-shortcut {
            font-size: 0.9rem;
            color: var(--gray);
            background: var(--light-gray);
            padding: 2px 8px;
            border-radius: 20px;
        }

        .control-actions {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px 0;
            border-top: 1px solid var(--light-gray);
        }

        .start-index {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .start-index label {
            font-weight: 500;
        }

        .start-index input {
            width: 80px;
            padding: 8px 12px;
            border: 1px solid var(--light-gray);
            border-radius: var(--border-radius);
            font-size: 1rem;
        }

        #stopAllBtn {
            background-color: var(--warning);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: var(--border-radius);
            cursor: pointer;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 8px;
            transition: var(--transition);
        }

        #stopAllBtn:hover {
            background-color: #d3166d;
        }

        /* 爬虫状态面板 */
        .status-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            padding: 20px;
        }

        .status-card {
            border: 1px solid var(--light-gray);
            border-radius: var(--border-radius);
            overflow: hidden;
            transition: var(--transition);
        }

        .status-card:hover {
            transform: translateY(-3px);
            box-shadow: var(--shadow);
        }

        .status-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px;
            background-color: var(--light-gray);
        }

        .crawler-name {
            font-weight: 600;
            font-size: 1.1rem;
        }

        .status-indicator {
            padding: 5px 12px;
            border-radius: 20px;
            font-size: 0.9rem;
            font-weight: 500;
        }

        .status-indicator.idle {
            background-color: #e9ecef;
            color: var(--gray);
        }

        .status-indicator.running {
            background-color: #d1e7dd;
            color: #0f5132;
        }

        .status-indicator.error {
            background-color: #f8d7da;
            color: #842029;
        }

        .status-details {
            padding: 15px;
        }

        .status-item {
            display: flex;
            justify-content: space-between;
            margin-bottom: 10px;
        }

        .status-item:last-child {
            margin-bottom: 0;
        }

        .label {
            color: var(--gray);
        }

        .value {
            font-weight: 500;
        }

        /* 日志面板 */
        .logs-section {
            padding: 0;
        }

        .logs-header {
            padding: 15px 20px;
            border-bottom: 1px solid var(--light-gray);
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 15px;
        }

        .log-tabs {
            display: flex;
            gap: 5px;
            flex-wrap: wrap;
        }

        .log-tab {
            padding: 8px 15px;
            background-color: var(--light-gray);
            border: none;
            border-radius: var(--border-radius);
            cursor: pointer;
            font-weight: 500;
            transition: var(--transition);
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .log-tab.active {
            background-color: var(--primary);
            color: white;
        }

        .log-tab:hover:not(.active) {
            background-color: #d7e1ff;
        }

        .log-tab .badge {
            background-color: var(--warning);
            color: white;
            border-radius: 10px;
            padding: 2px 8px;
            font-size: 0.8rem;
            display: none;
        }

        .log-tab.has-updates .badge {
            display: inline-block;
        }

        .log-actions {
            display: flex;
            gap: 10px;
            align-items: center;
        }

        .action-btn {
            display: flex;
            align-items: center;
            gap: 5px;
            padding: 8px 15px;
            background-color: var(--light-gray);
            border: none;
            border-radius: var(--border-radius);
            cursor: pointer;
            transition: var(--transition);
        }

        .action-btn:hover {
            background-color: #d7e1ff;
        }

        .auto-scroll-toggle {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 0.9rem;
        }

        .logs-container {
            padding: 20px;
            min-height: 300px;
            max-height: 400px;
            overflow: auto;
        }

        .log-panel {
            display: none;
        }

        .log-panel.active {
            display: block;
        }

        .log-content {
            background-color: #f8f9fa;
            border-radius: var(--border-radius);
            padding: 15px;
            font-family: 'Courier New', Courier, monospace;
            font-size: 0.9rem;
            white-space: pre-wrap;
            max-height: 300px;
            overflow: auto;
        }

        .log-entry {
            margin-bottom: 8px;
            padding-bottom: 8px;
            border-bottom: 1px solid #e9ecef;
        }

        .log-entry:last-child {
            margin-bottom: 0;
            border-bottom: none;
        }

        .log-timestamp {
            color: var(--gray);
            font-size: 0.85rem;
            margin-right: 10px;
        }

        .log-message {
            color: var(--dark);
        }

        .log-message.info {
            color: #0d6efd;
        }

        .log-message.warning {
            color: #ffc107;
        }

        .log-message.error {
            color: #dc3545;
        }

        .log-message.success {
            color: #198754;
        }

        /* 结果查看器 */
        .results-header {
            padding: 15px 20px;
            border-bottom: 1px solid var(--light-gray);
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 15px;
        }

        .results-controls {
            display: flex;
            gap: 10px;
            align-items: center;
        }

        #resultType {
            padding: 8px 15px;
            border: 1px solid var(--light-gray);
            border-radius: var(--border-radius);
            font-size: 1rem;
        }

        .results-container {
            padding: 20px;
        }

        .results-info {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 20px;
            margin-bottom: 20px;
            padding-bottom: 20px;
            border-bottom: 1px solid var(--light-gray);
        }

        .info-item {
            display: flex;
            justify-content: space-between;
            background-color: var(--light-gray);
            padding: 12px 15px;
            border-radius: var(--border-radius);
        }

        .label {
            font-weight: 500;
        }

        .viewer-controls {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }

        .nav-btn {
            padding: 10px 20px;
            background-color: var(--light-gray);
            border: none;
            border-radius: var(--border-radius);
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: 8px;
            transition: var(--transition);
        }

        .nav-btn:hover {
            background-color: #d7e1ff;
        }

        .nav-btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .author-info {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        #currentAuthor {
            font-weight: 600;
            font-size: 1.1rem;
        }

        #authorIndex {
            color: var(--gray);
            font-size: 0.9rem;
        }

        .author-card {
            background-color: var(--light-gray);
            border-radius: var(--border-radius);
            padding: 25px;
            min-height: 300px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }

        .card-placeholder {
            text-align: center;
            color: var(--gray);
        }

        .placeholder-icon {
            font-size: 4rem;
            margin-bottom: 15px;
            opacity: 0.3;
        }

        .placeholder-text {
            font-size: 1.1rem;
        }

        .author-details {
            width: 100%;
        }

        .author-header {
            display: flex;
            align-items: center;
            gap: 15px;
            margin-bottom: 20px;
        }

        .author-avatar {
            width: 80px;
            height: 80px;
            border-radius: 50%;
            background-color: var(--primary);
            display: flex;
            justify-content: center;
            align-items: center;
            color: white;
            font-size: 2rem;
        }

        .author-name {
            font-size: 1.5rem;
            font-weight: 600;
        }

        .author-id {
            color: var(--gray);
            font-size: 0.9rem;
        }

        .author-stats {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 15px;
            margin-bottom: 25px;
        }

        .stat-card {
            background: white;
            border-radius: var(--border-radius);
            padding: 15px;
            text-align: center;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
        }

        .stat-value {
            font-size: 1.8rem;
            font-weight: 700;
            color: var(--primary);
            margin-bottom: 5px;
        }

        .stat-label {
            color: var(--gray);
            font-size: 0.9rem;
        }

        .author-data {
            background: white;
            border-radius: var(--border-radius);
            padding: 20px;
            margin-top: 20px;
        }

        .data-section {
            margin-bottom: 20px;
        }

        .data-section:last-child {
            margin-bottom: 0;
        }

        .data-title {
            font-size: 1.1rem;
            font-weight: 600;
            margin-bottom: 10px;
            padding-bottom: 5px;
            border-bottom: 1px solid var(--light-gray);
        }

        .data-content {
            padding-left: 15px;
        }

        .data-item {
            margin-bottom: 8px;
        }

        .data-label {
            font-weight: 500;
            margin-right: 8px;
        }

        /* 通知容器 */
        .notification-container {
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 1000;
            max-width: 350px;
        }

        .notification {
            background: white;
            border-radius: var(--border-radius);
            box-shadow: var(--shadow);
            padding: 15px;
            margin-bottom: 15px;
            display: flex;
            align-items: flex-start;
            gap: 12px;
            animation: slideIn 0.3s ease-out;
            position: relative;
            overflow: hidden;
        }

        .notification::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 5px;
            height: 100%;
        }

        .notification.success {
            border-left: 5px solid #198754;
        }

        .notification.error {
            border-left: 5px solid #dc3545;
        }

        .notification.warning {
            border-left: 5px solid #ffc107;
        }

        .notification.info {
            border-left: 5px solid #0d6efd;
        }

        .notification-icon {
            font-size: 1.5rem;
        }

        .notification.success .notification-icon {
            color: #198754;
        }

        .notification.error .notification-icon {
            color: #dc3545;
        }

        .notification.warning .notification-icon {
            color: #ffc107;
        }

        .notification.info .notification-icon {
            color: #0d6efd;
        }

        .notification-content {
            flex: 1;
        }

        .notification-title {
            font-weight: 600;
            margin-bottom: 5px;
        }

        .notification-message {
            font-size: 0.9rem;
            color: var(--gray);
        }

        .notification-close {
            background: none;
            border: none;
            cursor: pointer;
            color: var(--gray);
            font-size: 1.2rem;
            padding: 0;
        }

        @keyframes slideIn {
            from {
                transform: translateX(100%);
                opacity: 0;
            }
            to {
                transform: translateX(0);
                opacity: 1;
            }
        }

        /* 响应式调整 */
        @media (max-width: 768px) {
            .crawler-buttons {
                grid-template-columns: 1fr;
            }
            
            .status-grid {
                grid-template-columns: 1fr;
            }
            
            .results-header {
                flex-direction: column;
                align-items: flex-start;
            }
            
            .results-controls {
                width: 100%;
                justify-content: space-between;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1><i class="fas fa-robot"></i> 多爬虫管理系统</h1>
            <p>同时管理多个爬虫任务，实时监控日志和状态</p>
        </header>

        <!-- 爬虫控制面板 -->
        <section class="crawler-controls">
            <h2><i class="fas fa-gamepad"></i> 爬虫控制</h2>
            <div class="crawler-buttons">
                <button class="crawler-btn" data-type="semantic_scholar" data-name="Semantic Scholar">
                    <span class="btn-icon"><i class="fas fa-book"></i></span>
                    <span class="btn-text">Semantic Scholar</span>
                    <span class="btn-shortcut">Ctrl+1</span>
                </button>
                <button class="crawler-btn" data-type="scholar" data-name="Scholar">
                    <span class="btn-icon"><i class="fas fa-user-graduate"></i></span>
                    <span class="btn-text">Scholar</span>
                    <span class="btn-shortcut">Ctrl+2</span>
                </button>
                <button class="crawler-btn" data-type="dblp" data-name="DBLP">
                    <span class="btn-icon"><i class="fas fa-database"></i></span>
                    <span class="btn-text">DBLP</span>
                    <span class="btn-shortcut">Ctrl+3</span>
                </button>
                <button class="crawler-btn test" data-type="test" data-name="测试爬虫">
                    <span class="btn-icon"><i class="fas fa-flask"></i></span>
                    <span class="btn-text">测试爬虫</span>
                    <span class="btn-shortcut">Ctrl+4</span>
                </button>
            </div>
            
            <div class="control-actions">
                <div class="start-index">
                    <label for="startIndex">起始索引:</label>
                    <input type="number" id="startIndex" value="1" min="1">
                </div>
                <button id="stopAllBtn" class="stop-btn">
                    <span class="btn-icon"><i class="fas fa-stop"></i></span>
                    <span class="btn-text">停止所有</span>
                    <span class="btn-shortcut">Ctrl+S</span>
                </button>
            </div>
        </section>

        <!-- 爬虫状态面板 -->
        <section class="crawler-status">
            <h2><i class="fas fa-chart-line"></i> 爬虫状态</h2>
            <div class="status-grid">
                <div class="status-card" data-crawler="semantic_scholar">
                    <div class="status-header">
                        <span class="crawler-name">Semantic Scholar</span>
                        <span class="status-indicator idle">空闲</span>
                    </div>
                    <div class="status-details">
                        <div class="status-item">
                            <span class="label">状态:</span>
                            <span class="value status-text">空闲</span>
                        </div>
                        <div class="status-item">
                            <span class="label">运行时间:</span>
                            <span class="value runtime">-</span>
                        </div>
                        <div class="status-item">
                            <span class="label">处理数量:</span>
                            <span class="value processed">0</span>
                        </div>
                    </div>
                </div>
                
                <div class="status-card" data-crawler="scholar">
                    <div class="status-header">
                        <span class="crawler-name">Scholar</span>
                        <span class="status-indicator idle">空闲</span>
                    </div>
                    <div class="status-details">
                        <div class="status-item">
                            <span class="label">状态:</span>
                            <span class="value status-text">空闲</span>
                        </div>
                        <div class="status-item">
                            <span class="label">运行时间:</span>
                            <span class="value runtime">-</span>
                        </div>
                        <div class="status-item">
                            <span class="label">处理数量:</span>
                            <span class="value processed">0</span>
                        </div>
                    </div>
                </div>
                
                <div class="status-card" data-crawler="dblp">
                    <div class="status-header">
                        <span class="crawler-name">DBLP</span>
                        <span class="status-indicator idle">空闲</span>
                    </div>
                    <div class="status-details">
                        <div class="status-item">
                            <span class="label">状态:</span>
                            <span class="value status-text">空闲</span>
                        </div>
                        <div class="status-item">
                            <span class="label">运行时间:</span>
                            <span class="value runtime">-</span>
                        </div>
                        <div class="status-item">
                            <span class="label">处理数量:</span>
                            <span class="value processed">0</span>
                        </div>
                    </div>
                </div>
                
                <div class="status-card" data-crawler="test">
                    <div class="status-header">
                        <span class="crawler-name">测试爬虫</span>
                        <span class="status-indicator idle">空闲</span>
                    </div>
                    <div class="status-details">
                        <div class="status-item">
                            <span class="label">状态:</span>
                            <span class="value status-text">空闲</span>
                        </div>
                        <div class="status-item">
                            <span class="label">运行时间:</span>
                            <span class="value runtime">-</span>
                        </div>
                        <div class="status-item">
                            <span class="label">处理数量:</span>
                            <span class="value processed">0</span>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- 日志面板 -->
        <section class="logs-section">
            <div class="logs-header">
                <h2><i class="fas fa-clipboard-list"></i> 实时日志</h2>
                <div class="log-actions">
                    <div class="log-tabs">
                        <button class="log-tab active" data-crawler="semantic_scholar">
                            <i class="fas fa-book"></i>
                            <span>Semantic Scholar</span>
                            <span class="badge">0</span>
                        </button>
                        <button class="log-tab" data-crawler="scholar">
                            <i class="fas fa-user-graduate"></i>
                            <span>Scholar</span>
                            <span class="badge">0</span>
                        </button>
                        <button class="log-tab" data-crawler="dblp">
                            <i class="fas fa-database"></i>
                            <span>DBLP</span>
                            <span class="badge">0</span>
                        </button>
                        <button class="log-tab" data-crawler="test">
                            <i class="fas fa-flask"></i>
                            <span>测试爬虫</span>
                            <span class="badge">0</span>
                        </button>
                    </div>
                    <div class="log-actions">
                        <button id="testLogsBtn" class="action-btn">
                            <i class="fas fa-vial"></i>
                            <span>测试日志</span>
                        </button>
                        <button id="clearLogsBtn" class="action-btn">
                            <i class="fas fa-trash"></i>
                            <span>清空日志</span>
                        </button>
                        <button id="exportLogsBtn" class="action-btn">
                            <i class="fas fa-download"></i>
                            <span>导出日志</span>
                        </button>
                        <label class="auto-scroll-toggle">
                            <input type="checkbox" id="autoScroll" checked>
                            <span class="toggle-text">自动滚动</span>
                        </label>
                    </div>
                </div>
            </div>
            
            <div class="logs-container">
                <div class="log-panel active" data-crawler="semantic_scholar">
                    <div class="log-content" id="semantic_scholar_logs"></div>
                </div>
                <div class="log-panel" data-crawler="scholar">
                    <div class="log-content" id="scholar_logs"></div>
                </div>
                <div class="log-panel" data-crawler="dblp">
                    <div class="log-content" id="dblp_logs"></div>
                </div>
                <div class="log-panel" data-crawler="test">
                    <div class="log-content" id="test_logs"></div>
                </div>
            </div>
        </section>

        <!-- 结果查看器 -->
        <section class="results-section">
            <h2><i class="fas fa-chart-bar"></i> 结果查看器</h2>
            <div class="results-header">
                <div class="results-controls">
                    <select id="resultType">
                        <option value="semantic_scholar">Semantic Scholar</option>
                        <option value="scholar">Scholar</option>
                        <option value="dblp">DBLP</option>
                    </select>
                    <button id="loadResultsBtn" class="action-btn">
                        <i class="fas fa-sync-alt"></i>
                        <span>加载结果</span>
                    </button>
                </div>
            </div>
            
            <div class="results-container">
                <div class="results-info">
                    <div class="info-item">
                        <span class="label">文件状态:</span>
                        <span class="value" id="fileStatus">未加载</span>
                    </div>
                    <div class="info-item">
                        <span class="label">记录数量:</span>
                        <span class="value" id="recordCount">0</span>
                    </div>
                    <div class="info-item">
                        <span class="label">文件大小:</span>
                        <span class="value" id="fileSize">0 KB</span>
                    </div>
                </div>
                
                <div class="viewer-controls">
                    <button id="prevAuthor" class="nav-btn" disabled>
                        <i class="fas fa-arrow-left"></i>
                        <span>上一个</span>
                    </button>
                    <div class="author-info">
                        <span id="currentAuthor">未选择作者</span>
                        <span id="authorIndex">0 / 0</span>
                    </div>
                    <button id="nextAuthor" class="nav-btn" disabled>
                        <span>下一个</span>
                        <i class="fas fa-arrow-right"></i>
                    </button>
                </div>
                
                <div class="author-card" id="authorCard">
                    <div class="card-placeholder">
                        <span class="placeholder-icon"><i class="fas fa-user"></i></span>
                        <span class="placeholder-text">请加载结果文件查看作者信息</span>
                    </div>
                </div>
            </div>
        </section>
    </div>

    <!-- 通知容器 -->
    <div id="notificationContainer" class="notification-container"></div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
    <script>
        // 全局变量
        let socket;
        let currentResults = [];
        let currentIndex = 0;
        let autoScrollEnabled = true;
        let newLogCounts = {
            semantic_scholar: 0,
            scholar: 0,
            dblp: 0,
            test: 0
        };

        // DOM 加载完成后执行
        document.addEventListener('DOMContentLoaded', function() {
            // 初始化Socket连接
            initSocket();
            
            // 初始化事件监听器
            initEventListeners();
        });

        // 初始化Socket连接
        function initSocket() {
            // 连接到Socket.IO服务器
            socket = io.connect('http://' + document.domain + ':' + location.port);
            
            // 连接成功事件
            socket.on('connect', function() {
                showNotification('连接成功', 'WebSocket连接已建立', 'success');
                console.log('WebSocket连接已建立');
            });
            
            // 连接断开事件
            socket.on('disconnect', function() {
                showNotification('连接断开', 'WebSocket连接已断开', 'warning');
                console.log('WebSocket连接已断开');
            });
            
            // 日志消息事件
            socket.on('log_message', function(data) {
                addLogEntry(data.crawler_type, data.message, data.type, data.timestamp);
            });
            
            // 爬虫状态更新事件
            socket.on('crawler_status', function(data) {
                updateCrawlerStatus(data.crawler_type, data.status, data.message);
            });
        }

        // 初始化事件监听器
        function initEventListeners() {
            // 爬虫启动按钮
            document.querySelectorAll('.crawler-btn').forEach(button => {
                button.addEventListener('click', function() {
                    const crawlerType = this.dataset.type;
                    const crawlerName = this.dataset.name;
                    const startIndex = parseInt(document.getElementById('startIndex').value) || 1;
                    
                    startCrawler(crawlerType, crawlerName, startIndex);
                });
            });
            
            // 停止所有爬虫按钮
            document.getElementById('stopAllBtn').addEventListener('click', stopAllCrawlers);
            
            // 清空日志按钮
            document.getElementById('clearLogsBtn').addEventListener('click', clearAllLogs);
            
            // 测试日志按钮
            document.getElementById('testLogsBtn').addEventListener('click', testLogs);
            
            // 导出日志按钮
            document.getElementById('exportLogsBtn').addEventListener('click', exportLogs);
            
            // 自动滚动切换
            document.getElementById('autoScroll').addEventListener('change', function() {
                autoScrollEnabled = this.checked;
            });
            
            // 加载结果按钮
            document.getElementById('loadResultsBtn').addEventListener('click', loadResults);
            
            // 上一个作者按钮
            document.getElementById('prevAuthor').addEventListener('click', showPrevAuthor);
            
            // 下一个作者按钮
            document.getElementById('nextAuthor').addEventListener('click', showNextAuthor);
            
            // 日志标签切换
            document.querySelectorAll('.log-tab').forEach(tab => {
                tab.addEventListener('click', function() {
                    const crawlerType = this.dataset.crawler;
                    switchLogTab(crawlerType);
                });
            });
        }

        // 启动爬虫
        function startCrawler(crawlerType, crawlerName, startIndex) {
            showNotification('爬虫启动', `正在启动 ${crawlerName} 爬虫`, 'info');
            
            // 发送启动请求
            fetch('/api/start_crawler', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    crawler_type: crawlerType,
                    start_index: startIndex
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    showNotification('爬虫启动成功', data.message, 'success');
                } else {
                    showNotification('爬虫启动失败', data.message, 'error');
                }
            })
            .catch(error => {
                showNotification('请求错误', '无法启动爬虫: ' + error.message, 'error');
            });
        }

        // 停止所有爬虫
        function stopAllCrawlers() {
            showNotification('停止爬虫', '正在停止所有爬虫任务', 'warning');
            
            fetch('/api/stop_crawler', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    crawler_type: 'all'
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    showNotification('爬虫已停止', data.message, 'success');
                } else {
                    showNotification('停止失败', data.message, 'error');
                }
            })
            .catch(error => {
                showNotification('请求错误', '无法停止爬虫: ' + error.message, 'error');
            });
        }

        // 添加日志条目
        function addLogEntry(crawlerType, message, type, timestamp) {
            const logElement = document.getElementById(`${crawlerType}_logs`);
            if (!logElement) return;
            
            const logEntry = document.createElement('div');
            logEntry.className = 'log-entry';
            
            const timestampSpan = document.createElement('span');
            timestampSpan.className = 'log-timestamp';
            timestampSpan.textContent = `[${timestamp}] `;
            logEntry.appendChild(timestampSpan);
            
            const messageSpan = document.createElement('span');
            messageSpan.className = `log-message ${type}`;
            messageSpan.textContent = message;
            logEntry.appendChild(messageSpan);
            
            logElement.appendChild(logEntry);
            
            // 获取当前激活的日志面板
            const activePanel = document.querySelector('.log-panel.active');
            const isActive = activePanel.dataset.crawler === crawlerType;
            
            if (isActive && autoScrollEnabled) {
                // 如果当前激活且自动滚动开启，则滚动到底部
                logElement.scrollTop = logElement.scrollHeight;
            } else if (!isActive) {
                // 如果不是激活状态，增加该爬虫类型的新日志计数
                newLogCounts[crawlerType] += 1;
                updateLogTabBadge(crawlerType, newLogCounts[crawlerType]);
            }
        }

        // 更新日志标签的徽章
        function updateLogTabBadge(crawlerType, count) {
            const tab = document.querySelector(`.log-tab[data-crawler="${crawlerType}"] .badge`);
            if (tab) {
                tab.textContent = count;
                tab.style.display = count > 0 ? 'inline-block' : 'none';
                
                // 添加/移除更新指示器
                const tabButton = tab.closest('.log-tab');
                if (count > 0) {
                    tabButton.classList.add('has-updates');
                } else {
                    tabButton.classList.remove('has-updates');
                }
            }
        }

        // 切换日志标签页
        function switchLogTab(crawlerType) {
            // 隐藏所有日志面板
            document.querySelectorAll('.log-panel').forEach(panel => {
                panel.classList.remove('active');
            });
            // 显示选中的日志面板
            const activePanel = document.querySelector(`.log-panel[data-crawler="${crawlerType}"]`);
            if (activePanel) {
                activePanel.classList.add('active');
            }

            // 更新标签页激活状态
            document.querySelectorAll('.log-tab').forEach(tab => {
                tab.classList.remove('active');
            });
            const activeTab = document.querySelector(`.log-tab[data-crawler="${crawlerType}"]`);
            if (activeTab) {
                activeTab.classList.add('active');
            }

            // 重置该爬虫类型的新日志计数
            newLogCounts[crawlerType] = 0;
            updateLogTabBadge(crawlerType, 0);

            // 滚动到底部（如果自动滚动开启）
            if (autoScrollEnabled) {
                const logContent = activePanel.querySelector('.log-content');
                if (logContent) {
                    logContent.scrollTop = logContent.scrollHeight;
                }
            }
        }

        // 清空所有日志
        function clearAllLogs() {
            document.querySelectorAll('.log-content').forEach(logElement => {
                logElement.innerHTML = '';
            });
            showNotification('日志已清空', '所有爬虫日志已被清除', 'success');
        }

        // 测试日志
        function testLogs() {
            const crawlers = ['semantic_scholar', 'scholar', 'dblp', 'test'];
            const types = ['info', 'warning', 'error', 'success'];
            const messages = [
                '这是一条测试日志消息',
                '正在处理第 42 个文档',
                '遇到网络错误，正在重试...',
                '成功获取作者信息',
                '数据库连接超时',
                '处理完成，共 15 个记录'
            ];
            
            for (let i = 0; i < 10; i++) {
                const crawlerType = crawlers[Math.floor(Math.random() * crawlers.length)];
                const type = types[Math.floor(Math.random() * types.length)];
                const message = messages[Math.floor(Math.random() * messages.length)];
                const timestamp = new Date().toLocaleTimeString();
                
                addLogEntry(crawlerType, message, type, timestamp);
            }
            
            showNotification('测试日志', '已生成10条测试日志', 'info');
        }

        // 导出日志
        function exportLogs() {
            showNotification('导出日志', '日志导出功能正在开发中', 'info');
        }

        // 更新爬虫状态
        function updateCrawlerStatus(crawlerType, status, message) {
            const statusCard = document.querySelector(`.status-card[data-crawler="${crawlerType}"]`);
            if (!statusCard) return;
            
            // 更新状态指示器
            const indicator = statusCard.querySelector('.status-indicator');
            indicator.textContent = status === 'started' ? '运行中' : 
                                  status === 'completed' ? '已完成' : 
                                  status === 'stopped' ? '已停止' : 
                                  status === 'error' ? '错误' : '空闲';
            
            indicator.className = 'status-indicator ' + 
                (status === 'started' ? 'running' : 
                 status === 'completed' ? 'success' : 
                 status === 'error' ? 'error' : 'idle');
            
            // 更新状态文本
            statusCard.querySelector('.status-text').textContent = 
                status === 'started' ? '运行中' : 
                status === 'completed' ? '已完成' : 
                status === 'stopped' ? '已停止' : 
                status === 'error' ? '错误' : '空闲';
            
            // 更新开始时间
            if (status === 'started') {
                statusCard.querySelector('.runtime').textContent = new Date().toLocaleTimeString();
            }
            
            // 显示通知
            if (status === 'started') {
                showNotification('爬虫启动', message, 'info');
            } else if (status === 'completed') {
                showNotification('爬虫完成', message, 'success');
            } else if (status === 'stopped') {
                showNotification('爬虫停止', message, 'warning');
            } else if (status === 'error') {
                showNotification('爬虫错误', message, 'error');
            }
        }

        // 加载结果
        function loadResults() {
            const resultType = document.getElementById('resultType').value;
            
            fetch(`/api/results/${resultType}`)
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    currentResults = data.results;
                    currentIndex = 0;
                    
                    document.getElementById('fileStatus').textContent = '已加载';
                    document.getElementById('recordCount').textContent = data.count;
                    document.getElementById('fileSize').textContent = `${Math.round(data.count * 0.5)} KB`; // 模拟文件大小
                    
                    document.getElementById('prevAuthor').disabled = false;
                    document.getElementById('nextAuthor').disabled = false;
                    
                    showCurrentAuthor();
                    showNotification('结果加载', `成功加载 ${data.count} 条记录`, 'success');
                } else {
                    showNotification('加载失败', data.message, 'error');
                }
            })
            .catch(error => {
                showNotification('请求错误', '无法加载结果: ' + error.message, 'error');
            });
        }

        // 显示当前作者
        function showCurrentAuthor() {
            if (currentResults.length === 0) return;
            
            const authorCard = document.getElementById('authorCard');
            const author = currentResults[currentIndex];
            
            // 清空卡片
            authorCard.innerHTML = '';
            
            // 创建作者卡片
            const card = document.createElement('div');
            card.className = 'author-details';
            
            // 作者头部信息
            const header = document.createElement('div');
            header.className = 'author-header';
            
            const avatar = document.createElement('div');
            avatar.className = 'author-avatar';
            avatar.innerHTML = '<i class="fas fa-user"></i>';
            header.appendChild(avatar);
            
            const nameDiv = document.createElement('div');
            const name = document.createElement('div');
            name.className = 'author-name';
            name.textContent = author.name || '未知作者';
            nameDiv.appendChild(name);
            
            const id = document.createElement('div');
            id.className = 'author-id';
            id.textContent = `ID: ${author.id || 'N/A'}`;
            nameDiv.appendChild(id);
            
            header.appendChild(nameDiv);
            card.appendChild(header);
            
            // 作者统计信息
            const stats = document.createElement('div');
            stats.className = 'author-stats';
            
            const papersStat = createStatCard(author.paperCount || 0, '论文数');
            stats.appendChild(papersStat);
            
            const citationsStat = createStatCard(author.citationCount || 0, '引用数');
            stats.appendChild(citationsStat);
            
            const hIndexStat = createStatCard(author.hIndex || 0, 'H指数');
            stats.appendChild(hIndexStat);
            
            card.appendChild(stats);
            
            // 作者详细信息
            const details = document.createElement('div');
            details.className = 'author-data';
            
            const affiliations = createDataSection('所属机构', author.affiliations || ['未知']);
            details.appendChild(affiliations);
            
            const homepage = createDataSection('个人主页', author.homepage ? [author.homepage] : ['未知']);
            details.appendChild(homepage);
            
            const fields = createDataSection('研究领域', author.fieldsOfStudy || ['未知']);
            details.appendChild(fields);
            
            card.appendChild(details);
            
            authorCard.appendChild(card);
            
            // 更新索引显示
            document.getElementById('currentAuthor').textContent = author.name || '未知作者';
            document.getElementById('authorIndex').textContent = `${currentIndex + 1} / ${currentResults.length}`;
        }

        // 创建统计卡片
        function createStatCard(value, label) {
            const statCard = document.createElement('div');
            statCard.className = 'stat-card';
            
            const valueDiv = document.createElement('div');
            valueDiv.className = 'stat-value';
            valueDiv.textContent = value;
            statCard.appendChild(valueDiv);
            
            const labelDiv = document.createElement('div');
            labelDiv.className = 'stat-label';
            labelDiv.textContent = label;
            statCard.appendChild(labelDiv);
            
            return statCard;
        }

        // 创建数据部分
        function createDataSection(title, items) {
            const section = document.createElement('div');
            section.className = 'data-section';
            
            const titleDiv = document.createElement('div');
            titleDiv.className = 'data-title';
            titleDiv.textContent = title;
            section.appendChild(titleDiv);
            
            const content = document.createElement('div');
            content.className = 'data-content';
            
            items.forEach(item => {
                const itemDiv = document.createElement('div');
                itemDiv.className = 'data-item';
                itemDiv.textContent = item;
                content.appendChild(itemDiv);
            });
            
            section.appendChild(content);
            return section;
        }

        // 显示上一个作者
        function showPrevAuthor() {
            if (currentIndex > 0) {
                currentIndex--;
                showCurrentAuthor();
            }
        }

        // 显示下一个作者
        function showNextAuthor() {
            if (currentIndex < currentResults.length - 1) {
                currentIndex++;
                showCurrentAuthor();
            }
        }

        // 显示通知
        function showNotification(title, message, type) {
            const container = document.getElementById('notificationContainer');
            
            const notification = document.createElement('div');
            notification.className = `notification ${type}`;
            
            const icon = document.createElement('i');
            icon.className = 'notification-icon';
            
            if (type === 'success') icon.className += ' fas fa-check-circle';
            else if (type === 'error') icon.className += ' fas fa-exclamation-circle';
            else if (type === 'warning') icon.className += ' fas fa-exclamation-triangle';
            else icon.className += ' fas fa-info-circle';
            
            notification.appendChild(icon);
            
            const content = document.createElement('div');
            content.className = 'notification-content';
            
            const titleDiv = document.createElement('div');
            titleDiv.className = 'notification-title';
            titleDiv.textContent = title;
            content.appendChild(titleDiv);
            
            const messageDiv = document.createElement('div');
            messageDiv.className = 'notification-message';
            messageDiv.textContent = message;
            content.appendChild(messageDiv);
            
            notification.appendChild(content);
            
            const closeBtn = document.createElement('button');
            closeBtn.className = 'notification-close';
            closeBtn.innerHTML = '&times;';
            closeBtn.onclick = function() {
                notification.remove();
            };
            notification.appendChild(closeBtn);
            
            container.appendChild(notification);
            
            // 5秒后自动移除通知
            setTimeout(() => {
                if (notification.parentNode) {
                    notification.style.opacity = '0';
                    setTimeout(() => {
                        if (notification.parentNode) {
                            notification.remove();
                        }
                    }, 300);
                }
            }, 5000);
        }
    </script>
</body>
</html>